package org.toves.checklua.warnings;

import org.toves.checklua.ast.AstNode;
import org.toves.checklua.ast.AstProcessor;
import org.toves.checklua.ast.AstProcessorResult;
import org.toves.checklua.ast.Complaint;
import org.toves.checklua.ast.SimpleProcessorResult;
import org.toves.checklua.scan.LuaToken;
import org.toves.checklua.scan.Token;

public class UnreachableCodeProcessor implements AstProcessor {
	public UnreachableCodeProcessor() { }
	
	public void reset() { }
	
	public AstProcessorResult process(AstNode root) {
		SimpleProcessorResult result = new SimpleProcessorResult();
		descend(root, result);
		return result;
	}
	
	private AstNode descend(AstNode n, SimpleProcessorResult result) {
		AstNode ret = null;
		Object nType = n.getNodeType();
		if (nType == AstNode.SEQ) {
			for (int i = 0, parmCount = n.getParameterCount(); i < parmCount; i++) {
				AstNode sub = (AstNode) n.getParameter(i);
				if (ret != null) {
					Token token = sub.getRepresentativeToken();
					Object endType = ret.getNodeType();
					String msg = "code after " + endType.toString() + " is not reachable";
					
					if (endType == AstNode.BREAK || endType == AstNode.RETURN) {
						result.addComplaint(Complaint.createError(token, msg));
					} else {
						result.addComplaint(Complaint.createWarning(token, msg));
					}
				}

				AstNode subResult = descend(sub, result);
				if (ret == null && subResult != null) {
					ret = subResult;
				}
			}
		} else if (n.isStatement()) {
			if (nType == AstNode.BREAK || nType == AstNode.RETURN) {
				ret = n;
			} else if (nType == AstNode.WHILE) {
				AstNode cond = (AstNode) n.getParameter(0);
				if (cond.getNodeType() == AstNode.CONST) {
					Object val = cond.getRepresentativeToken().getTokenType();
					if (val == LuaToken.TRUE) {
						if (!findBreak((AstNode) n.getParameter(1))) {
							ret = n;
						}
					}
				}
			} else if (nType == AstNode.REPEAT) {
				AstNode cond = (AstNode) n.getParameter(1);
				if (cond.getNodeType() == AstNode.CONST) {
					Object val = cond.getRepresentativeToken().getTokenType();
					if (val == LuaToken.FALSE) {
						if (!findBreak((AstNode) n.getParameter(0))) {
							ret = n;
						}
					}
				}
			}
		}

		for (AstNode child : n.getChildren()) {
			descend(child, result);
		}
		return ret;
	}
	
	private boolean findBreak(AstNode n) {
		Object nType = n.getNodeType();
		if (nType == AstNode.SEQ) {
			for (int i = 0, parms = n.getParameterCount(); i < parms; i++) {
				if (findBreak((AstNode) n.getParameter(i))) {
					return true;
				}
			}
			return false;
		} else if (nType == AstNode.DO) {
			return findBreak((AstNode) n.getParameter(0));
		} else if (nType == AstNode.IF) {
			AstNode elseBody = (AstNode) n.getParameter(2);
			return findBreak((AstNode) n.getParameter(1))
				|| (elseBody != null && findBreak(elseBody));
		} else if (nType == AstNode.BREAK) {
			return true;
		} else {
			return false;
		}
	}
}
